package pl.edu.agh.pros.android;

import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;

import org.ros.RosCore;
import org.ros.android.view.camera.RosCameraPreviewView;
import org.ros.node.DefaultNodeMainExecutor;
import org.ros.node.NodeConfiguration;
import org.ros.node.NodeListener;
import org.ros.node.NodeMainExecutor;

import pl.edu.agh.pros.android.listener.UltimateNodeListener;
import pl.edu.agh.pros.android.ros.SensorListener;
import pl.edy.agh.pros.adk.util.ADKSendUtility;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.hardware.Camera;
import android.net.ConnectivityManager;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.util.Log;
import android.view.Window;
import android.view.WindowManager;

import com.android.future.usb.UsbAccessory;
import com.android.future.usb.UsbManager;

public class ServerActivity extends Activity {

	private final String TAG = "ServerActivity";
	private static final String ACTION_USB_PERMISSION = "pl.edu.agh.pros.bt.server.KoalaControllerServerActivity.action.USB_PERMISSION";

	private Camera mCamera;
	private RosCore rosCore;
	private RosCameraPreviewView preview;
	private NodeMainExecutor nodeMainExecutor;

	private SensorListener sensorListener;

	// ADK config
	private UsbManager mUsbManager;
	private PendingIntent mPermissionIntent;
	private boolean mPermissionRequestPending;
	private UsbAccessory mAccessory;
	private ParcelFileDescriptor mFileDescriptor;
	private FileOutputStream mOutputStream;
	private ADKSendUtility sendUtility = new ADKSendUtility();

	private PowerManager.WakeLock wl;

	private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			if (ACTION_USB_PERMISSION.equals(action)) {
				synchronized (this) {
					UsbAccessory accessory = UsbManager.getAccessory(intent);
					if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
						openAccessory(accessory);
					} else {
						Log.d(TAG, "permission denied for accessory " + accessory);
					}
					mPermissionRequestPending = false;
				}
			} else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
				UsbAccessory accessory = UsbManager.getAccessory(intent);
				if (accessory != null && accessory.equals(mAccessory)) {
					closeAccessory();
				}
			}
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		// Hide the window title.
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
		setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
		setContentView(R.layout.server);

		// Prepare ADK
		mUsbManager = UsbManager.getInstance(this);
		mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
		IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
		filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
		registerReceiver(mUsbReceiver, filter);

		if (getLastNonConfigurationInstance() != null) {
			mAccessory = (UsbAccessory) getLastNonConfigurationInstance();
			openAccessory(mAccessory);
		}

		// Getting a WakeLock. This insures that the phone does not sleep
		// while driving the robot.
		PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
		wl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "My Tag");
		wl.acquire();
	}

	@Override
	protected void onResume() {
		super.onResume();

		if (!MainActivity.isConnected(getApplicationContext(),
				(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE))) {
			finish();
			return;
		}

		UsbAccessory[] accessories = mUsbManager.getAccessoryList();
		UsbAccessory accessory = (accessories == null ? null : accessories[0]);
		if (accessory != null) {
			if (mUsbManager.hasPermission(accessory)) {
				openAccessory(accessory);
			} else {
				synchronized (mUsbReceiver) {
					if (!mPermissionRequestPending) {
						mUsbManager.requestPermission(accessory, mPermissionIntent);
						mPermissionRequestPending = true;
					}
				}
			}
		} else {
			Log.d(TAG, "mAccessory is null");
		}

		if (rosCore == null) {
			rosCore = RosCore.newPrivate();
		}
		rosCore.start();
		try {
			rosCore.awaitStart();
		} catch (Exception e) {
			Log.e(TAG, "Could not run rosCore", e);
			throw new RuntimeException(e);
		}

		preview = (RosCameraPreviewView) findViewById(R.id.ros_camera_preview_view);
		mCamera = Camera.open();
		preview.setCamera(mCamera);
		final NodeConfiguration nodeConfiguration = NodeConfiguration.newPrivate();
		nodeConfiguration.setMasterUri(rosCore.getUri());
		if (nodeMainExecutor == null) {
			nodeMainExecutor = DefaultNodeMainExecutor.newDefault();
		}

		nodeMainExecutor.execute(preview, nodeConfiguration.setNodeName("imagePublisher"),
				new ArrayList<NodeListener>() {
					private static final long serialVersionUID = 1L;
					{
						add(new UltimateNodeListener(nodeConfiguration, "/camera/image/compressed",
								sensor_msgs.CompressedImage._TYPE, true));
					};
				});

		sensorListener = new SensorListener(sendUtility);
		nodeMainExecutor.execute(sensorListener, nodeConfiguration.setNodeName("sensorListener"),
				new ArrayList<NodeListener>() {
					private static final long serialVersionUID = 1L;
					{
						add(new UltimateNodeListener(nodeConfiguration, "/koala/sensors", std_msgs.String._TYPE, false));
					};
				});
	}

	@Override
	public Object onRetainNonConfigurationInstance() {
		if (mAccessory != null) {
			return mAccessory;
		} else {
			return super.onRetainNonConfigurationInstance();
		}
	}

	@Override
	protected void onPause() {
		super.onPause();
		closeAccessory();
		if (preview != null) {
			preview.releaseCamera();
		}
		if (nodeMainExecutor != null) {
			nodeMainExecutor.shutdownNodeMain(preview);
			nodeMainExecutor.shutdownNodeMain(sensorListener);
			nodeMainExecutor.shutdown();
		}
		if (rosCore != null) {
			rosCore.shutdown();
		}
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		unregisterReceiver(mUsbReceiver);
		wl.release();
	}

	private void openAccessory(UsbAccessory accessory) {
		mFileDescriptor = mUsbManager.openAccessory(accessory);
		if (mFileDescriptor != null) {
			mAccessory = accessory;
			FileDescriptor fd = mFileDescriptor.getFileDescriptor();
			mOutputStream = new FileOutputStream(fd);
			sendUtility.setOutputStream(mOutputStream);
			Log.d(TAG, "accessory opened");
		} else {
			Log.d(TAG, "accessory open fail");
		}
	}

	private void closeAccessory() {
		try {
			if (mFileDescriptor != null) {
				mFileDescriptor.close();
			}
		} catch (IOException e) {
		} finally {
			mFileDescriptor = null;
			mAccessory = null;
		}
	}

}
